﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.Objects;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using MyFitness.Model;
using System.Windows.Forms.DataVisualization.Charting;
using System.Data.Entity.Infrastructure;

namespace MyFitness.View
{
    public partial class DailyInfoForm : Form
    {
        #region Declared Variables
        private Trainee trainee;
        private DateTime datetime;
        private MyFitnessDatabaseEntities database;
        #endregion

        /// <summary>
        /// Default Constructor.
        /// </summary>
        /// <param name="db">Database context used from the User Controller</param>
        /// <param name="trainee_email">The email used as an id for Trainee Object that Daily Information will be displayed for.</param>
        /// <param name="d">The Date of when to pull the Daily Information for a Trainee.</param>
        public DailyInfoForm(MyFitnessDatabaseEntities db, string trainee_email, DateTime d = default(DateTime))
        {
            try
            {
                // Ensures a valid trainee was passed into the function otherwise the form will exit.
                if (trainee_email.Length > 0 && db != null)
                {
                    database = db;
                    trainee = (Trainee)database.Users.Find(trainee_email);
                    
                    // If no DateTime parameter is specified in the constructor then it is set to Today's date.
                    if (d == default(DateTime))
                    {
                        d = DateTime.Now;
                    }

                    datetime = d;

                    InitializeComponent();
                    CenterToScreen();
                }
                else
                    Close();
                
                // Create handlers for tracking when Daily information is validated for a saving an entry.
                WeightTextBox.TextChanged += new EventHandler(OnEnableOKButton);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// Enables or Disables the OKButton depending on validated input.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void OnEnableOKButton(object sender, EventArgs e)
        {
            try
            {
                // Enables the "OK" Button if all the fields are validated, i.e. has a a weight greater than zero...
                if (Convert.ToDouble(WeightTextBox.Text) > 0)
                    okButton.Enabled = true;
                else
                    okButton.Enabled = false;
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                okButton.Enabled = false;
            }
        }

        /// <summary>
        /// Form Load Event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DailyInfoForm_Load(object sender, EventArgs e)
        {    
            if (trainee != null)
            {
                DailyInformationDateTimePicker.Value = datetime;

                // Checks to see if there are any trainee records for the date specified or it will create a new record.
                TraineeRecord tr = trainee.GetTraineeRecord(datetime);
                    
                // Only updates the text value of the WeightTextBox if the value is greater than zero.
                if (tr.Weight > 0)
                    WeightTextBox.Text = tr.Weight.ToString();

                // Loads all the exercises for a trainee record
                LoadExercises();

                // Loads all the meals for a trainee record
                LoadMeals();

                // Loads all the applicable infor from the exercises and the meals in to the chart.
                LoadChart();
            }
        }

        /// <summary>
        /// Adds an exercise to the ExerciseDataGridView.
        /// </summary>
        /// <param name="e">Exercise that is being added.</param>
        private void AddExerciseToDataGridView(Exercise e)
        {
            // Creating a new row in the control and then adding the exercise name into it.
            DataGridViewRow r = new DataGridViewRow();
            r.CreateCells(ExerciseDataGridView);

            // Ensures the exercise type is tied to the exercise, if not it looks up the value;
            if (e.ExerciseType == null)
            {
                ExerciseType et = database.ExerciseTypes.Find(e.ExerciseTypeID);
                r.Cells[0].Value = et.Name;
            }
            else
                r.Cells[0].Value = e.ExerciseType.Name;
            
            r.Cells[1].Value = e;
            ExerciseDataGridView.Rows.Add(r);
        }

        /// <summary>
        /// Adds a meal to the MealDataGridView.
        /// </summary>
        /// <param name="m"></param>
        private void AddMealToDataGridView(Meal m)
        {
            // Creating a new row in the control and then adding the meal name into it.
            DataGridViewRow r = new DataGridViewRow();
            r.CreateCells(MealDataGridView);

            // Ensures the meal type is tied to the meal, if not it looks up the value.
            if (m.MealType == null)
            {
                MealType mt = database.MealTypes.Find(m.MealTypeID);
                r.Cells[0].Value = mt.Name;
            }
            else
            {
                r.Cells[0].Value = m.MealType.Name;
            }

            r.Cells[1].Value = m;
            MealDataGridView.Rows.Add(r);
        }

        /// <summary>
        /// Loads all the exercise records for a TraineeRecord into the ExerciseDataGridView Control.
        /// </summary>
        private void LoadExercises()
        {
            ExerciseDataGridView.Rows.Clear();

            TraineeRecord tr = trainee.GetTraineeRecord(datetime, false);

            if (tr != null)
            {
                foreach (Exercise e in tr.Exercises)
                {
                    AddExerciseToDataGridView(e);
                }
            }
        }

        /// <summary>
        /// Loads all the meal records for a TraineeRecord into the MealDataGridView Control.
        /// </summary>
        private void LoadMeals()
        {
            MealDataGridView.Rows.Clear();

            TraineeRecord tr = trainee.GetTraineeRecord(datetime, false);

            if (tr != null)
            {
                foreach (Meal m in tr.Meals)
                {
                    AddMealToDataGridView(m);
                }
            }
        }

        /// <summary>
        /// Loads the DailyInfoChart with statistics from the meals and exercises.
        /// </summary>
        private void LoadChart()
        {
            try
            {
                // Clears the series values
                DailyInfoChart.Series["Calories Consumed"].Points.Clear();
                DailyInfoChart.Series["Calories Expended"].Points.Clear();

                // Gets the Data
                double caloriesconsumed = CalculateCaloriesConsumed();
                double caloriesexpended = CalculateCaloriesExpended();

                // Sets the Data
                DailyInfoChart.Series["Calories Consumed"].Points.Add(caloriesconsumed);
                DailyInfoChart.Series["Calories Expended"].Points.Add(caloriesexpended);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// Calculates all the calories consumed from the meals eaten.
        /// </summary>
        /// <returns></returns>
        private double CalculateCaloriesConsumed()
        {
            double? calories = 0;
            try
            {
                TraineeRecord tr = trainee.GetTraineeRecord(datetime, false);


                foreach (Meal m in tr.Meals)
                {
                    if( m.FoodAndBeverages.First().Calories.HasValue )
                        calories += Convert.ToDouble(m.Servings) * m.FoodAndBeverages.First().Calories;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            // Returns a zero for null values
            if (calories.HasValue == true)
                return calories.Value;
            else
                return 0;
        }

        /// <summary>
        /// Calculates all the calories expended from exercises.
        /// </summary>
        /// <returns></returns>
        private double CalculateCaloriesExpended()
        {
            double? calories = 0;
            try
            {
                TraineeRecord tr = trainee.GetTraineeRecord(datetime, false);


                foreach (Exercise e in tr.Exercises)
                {
                    if( e.CaloriesBurned.HasValue == true )
                        calories += e.CaloriesBurned;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }

            // Returns a zero for null values
            if (calories.HasValue == true)
                return calories.Value;
            else
                return 0;
        }

        /// <summary>
        /// OK Button Clicked Event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void okButton_Click(object sender, EventArgs e)
        {
            try
            {
                // Checks to see if there are any trainee records for the date specified andr it will not create a new record.
                TraineeRecord tr = trainee.GetTraineeRecord(datetime, false);

                if (tr != null)
                {
                    // Saving Values
                    tr.Weight = Convert.ToDouble(WeightTextBox.Text);
                }

                database.SaveChanges();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// Discards all the changes that were made to the database from this this form.
        /// </summary>
        private void DiscardChanges()
        {
            try
            {
                foreach (DbEntityEntry e in database.ChangeTracker.Entries())
                {
                    switch (e.State)
                    {
                        case EntityState.Added:
                            e.State = EntityState.Deleted;
                            e.Reload();
                            break;
                        case EntityState.Deleted:
                            e.State = EntityState.Unchanged;
                            e.Reload();
                            break;
                        default:
                            e.State = EntityState.Unchanged;
                            e.Reload();
                            break;
                    }
                }

                database.SaveChanges();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// Cancel Button Clicked Event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cancelButton_Click(object sender, EventArgs e)
        {
            DiscardChanges();
        }

        /// <summary>
        /// Daily Information DateTime Picker Value Changed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DailyInformationDateTimePicker_ValueChanged(object sender, EventArgs e)
        {
            datetime = DailyInformationDateTimePicker.Value;

            // Checks to see if there are any trainee records for the date specified or it will create a new record.
            TraineeRecord tr = trainee.GetTraineeRecord(datetime);

            // Updates Control Values
            WeightTextBox.Text = tr.Weight.ToString();

            // Loads the ExerciseDataGridView with data.
            LoadExercises();

            // Loads the MealDataGridView with data.
            LoadMeals();

            LoadChart();
        }

        /// <summary>
        /// Weight TextBox Changed Event.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void WeightTextBox_TextChanged(object sender, EventArgs e)
        {
            try
            {
                TraineeRecord tr = trainee.GetTraineeRecord(datetime);
                tr.Weight = Convert.ToDouble(WeightTextBox.Text);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// Adds a meal to the trainee record.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void AddMealButton_Click(object sender, EventArgs e)
        {
            try
            {
                MealEntryForm mealEntryForm = new MealEntryForm(database);
                DialogResult result = mealEntryForm.ShowDialog();

                if (result == DialogResult.OK)
                {
                    Meal meal = mealEntryForm.GetMealEntry();

                    if (meal != null)
                    {
                        TraineeRecord tr = trainee.GetTraineeRecord(datetime);
                        tr.Meals.Add(meal);
                        
                        // Updates the DataGridView with the meal.
                        AddMealToDataGridView(meal);

                        // Updates the Chart with the added meal
                        LoadChart();
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// Adds an exercise to the trainee record.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void AddExerciseButton_Click(object sender, EventArgs e)
        {
            try
            {
                ExerciseEntryForm exerciseEntryForm = new ExerciseEntryForm(database);
                DialogResult result = exerciseEntryForm.ShowDialog();

                if (result == DialogResult.OK)
                {
                    Exercise exercise = exerciseEntryForm.GetExerciseEntry();

                    if (exercise != null)
                    {
                        TraineeRecord tr = trainee.GetTraineeRecord(datetime);
                        tr.Exercises.Add(exercise);

                        // Updates the DataGridView with the exercise.
                        AddExerciseToDataGridView(exercise);

                        // Updates the Chart with the added exercise
                        LoadChart();
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// Deletes a Meal from the list of meals for a trainee record.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DeleteMeal_Click(object sender, EventArgs e)
        {
            try
            {
                if (MealDataGridView.SelectedCells.Count > 0)
                {
                    // Gets the current row index from the selected cell.
                    int rowindex = MealDataGridView.SelectedCells[0].RowIndex;

                    // Gets the selected row.
                    DataGridViewRow row = MealDataGridView.Rows[rowindex];

                    // Gets the trainee record selected from the date specified.
                    TraineeRecord tr = trainee.GetTraineeRecord(datetime);

                    // Gets the meal object that is saved in the Meal DataGridView control.
                    Meal meal = (Meal)row.Cells[1].Value;

                    // Removes the exercise from the trainnee record.
                    tr.Meals.Remove(meal);

                    database.Entry(meal).State = EntityState.Deleted;

                    // Removes the row from the MealDataGridView
                    MealDataGridView.Rows.Remove(row);

                    // Updates the Chart with the deleted meal
                    LoadChart();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// Deletes an Exercise from the list of exercises for a trainee record.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void DeleteExercise_Click(object sender, EventArgs e)
        {
            try
            {
                if (ExerciseDataGridView.SelectedCells.Count > 0)
                {
                    // Gets the current row index from the selected cell.
                    int rowindex = ExerciseDataGridView.SelectedCells[0].RowIndex;

                    // Gets the selected row.
                    DataGridViewRow row = ExerciseDataGridView.Rows[rowindex];

                    // Gets the trainee record selected from the date specified.
                    TraineeRecord tr = trainee.GetTraineeRecord(datetime);

                    // Gets the exercise object that is saved in the Exercise DataGridView control.
                    Exercise exercise = (Exercise)row.Cells[1].Value;
                    
                    // Removes the exercise from the trainnee record.
                    tr.Exercises.Remove(exercise);

                    database.Entry(exercise).State = EntityState.Deleted;
                    
                    // Removes the row from the ExerciseDataGridView
                    ExerciseDataGridView.Rows.Remove(row);

                    // Updates the Chart with the deleted exercise
                    LoadChart();
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// Edits a Meal from the list of meals for a trainee record.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void EditMeal_Click(object sender, EventArgs e)
        {
            try
            {
                if (MealDataGridView.SelectedCells.Count > 0)
                {
                    // Gets the current row index from the selected cell.
                    int rowindex = MealDataGridView.SelectedCells[0].RowIndex;

                    // Gets the selected row.
                    DataGridViewRow row = MealDataGridView.Rows[rowindex];

                    // Gets the trainee record selected from the date specified.
                    TraineeRecord tr = trainee.GetTraineeRecord(datetime);

                    // Gets the meal object that is saved in the Meal DataGridView control.
                    Meal meal = (Meal)row.Cells[1].Value;

                    MealEntryForm mealEntryForm = new MealEntryForm(database, meal);
                    DialogResult result = mealEntryForm.ShowDialog();

                    if (result == DialogResult.OK)
                    {
                        meal = mealEntryForm.GetMealEntry();

                        database.Entry(meal).State = EntityState.Modified;

                        // Reloads the meals in the DataGridView in case the meal name was changed.
                        LoadMeals();
                        LoadChart();
                    }
                    else
                        database.Entry(meal).State = EntityState.Unchanged;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// Edits an Exercise from the list of exercises for a trainee record.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void EditExercise_Click(object sender, EventArgs e)
        {
            try
            {
                if (ExerciseDataGridView.SelectedCells.Count > 0)
                {
                    // Gets the current row index from the selected cell.
                    int rowindex = ExerciseDataGridView.SelectedCells[0].RowIndex;

                    // Gets the selected row.
                    DataGridViewRow row = ExerciseDataGridView.Rows[rowindex];

                    // Gets the trainee record selected from the date specified.
                    TraineeRecord tr = trainee.GetTraineeRecord(datetime);

                    // Gets the exercise object that is saved in the Exercise DataGridView control.
                    Exercise exercise = (Exercise)row.Cells[1].Value;

                    ExerciseEntryForm exerciseEntryForm = new ExerciseEntryForm(database, exercise);
                    DialogResult result = exerciseEntryForm.ShowDialog();

                    if (result == DialogResult.OK)
                    {
                        exercise = exerciseEntryForm.GetExerciseEntry();

                        database.Entry(exercise).State = EntityState.Modified;

                        // Reloads the exercises in the DataGridView in case the exercise name was changed.
                        LoadExercises();
                        LoadChart();
                    }
                    else
                        database.Entry(exercise).State = EntityState.Unchanged;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }

        /// <summary>
        /// Traps for the mouse down event in the MealsDataGridView.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MealDataGridView_MouseDown(object sender, MouseEventArgs e)
        {
            // Logic to select row in the Meal DataGridView on Right click.
            if (e.Button == MouseButtons.Right)
            {
                DataGridView.HitTestInfo hit = MealDataGridView.HitTest(e.X, e.Y);
                if (hit.Type == DataGridViewHitTestType.Cell && hit.RowIndex >= 0)
                {
                    MealDataGridView.ClearSelection();
                    MealDataGridView.Rows[hit.RowIndex].Selected = true;
                }
            }
        }

        /// <summary>
        /// Traps for the mouse down event in the ExerciseDataGridView.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ExerciseDataGridView_MouseDown(object sender, MouseEventArgs e)
        {
            // Logic to select row in Exercise DataGridView on Right click.
            if (e.Button == MouseButtons.Right)
            {
                DataGridView.HitTestInfo hit = ExerciseDataGridView.HitTest(e.X, e.Y);
                if (hit.Type == DataGridViewHitTestType.Cell && hit.RowIndex >= 0)
                {
                    ExerciseDataGridView.ClearSelection();
                    ExerciseDataGridView.Rows[hit.RowIndex].Selected = true;
                }
            }
        }

        #region Accessors
        /// <summary>
        /// Returns a Trainee Object from the data in the form.
        /// </summary>
        public Trainee Trainee
        {
            get
            {
                return trainee;
            }
        }
        #endregion
    }
}
